package cn.infrabase.core.util;

import cn.infrabase.Errors;

import javax.annotation.Nullable;
import java.lang.reflect.Field;

/**
 * 反射工具类。
 *
 * @author Weimin Gao
 * @since 1.0.0
 **/
public class ReflectionUtils {

	/**
	 * 构造给定实例类型的对象。
	 *
	 * @param type 实例类型对象。
	 * @param <T>  实例类型。
	 * @return 实例对象。
	 */
	public static <T> T newInstance(Class<T> type) {
		try {
			return type.getDeclaredConstructor().newInstance();
		} catch (Exception e) {
			throw new ReflectionException(e, String.format("构造实例发生错误【%s】", e.getMessage()));
		}
	}

	/**
	 * 返回给定类中指定的字段。
	 *
	 * @param target 给定的类。
	 * @param name   给定类中字段的名称。
	 * @return 字段对象。
	 */
	public static Field getField(final Class<?> target, final String name) {
		Class<?> superclass;
		Class<?> clazz = target;
		String className = Object.class.getName();
		while (!className.equals(clazz.getName())) {
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {
				if (field.getName().equals(name)) {
					field.setAccessible(true);
					return field;
				}
			}
			superclass = clazz.getSuperclass();
			clazz = superclass;
		}
		return null;
	}

	/**
	 * 返回给定对象对应类中指定的字段。
	 *
	 * @param object 给定的对象。
	 * @param name   给定对象对应类中字段的名称。
	 * @return 字段对象。
	 */
	public static Field getField(final Object object, final String name) {
		Class<?> clazz = ObjectUtils.getClass(object);
		return getField(clazz, name);
	}

	/**
	 * 设置给定对象中字段的值。
	 *
	 * @param object 给定的对象。
	 * @param name   字段的名称。
	 * @param value  字段设置的值。
	 */
	public static void setField(final Object object, final String name, @Nullable final Object value) {
		Field field = getField(object, name);
		if (field == null) {
			throw new ReflectionGetFieldNotExistException(object, name);
		}
		setField(object, field, value);
	}

	/**
	 * 设置给定对象中字段的值。
	 *
	 * @param object 给定的对象。
	 * @param field  字段的名称。
	 * @param value  字段设置的值。
	 */
	public static void setField(final Object object, final Field field, @Nullable final Object value) {
		try {
			field.setAccessible(true);
			field.set(object, value);
		} catch (Exception e) {
			throw new ReflectionSetFieldValueException(object, field);
		}
	}

}
